package ga

import (
	"fmt"
	"math/rand"
)

//主进程的运行
type MainProcessInterface interface {
	//运行
	Run(num, length, maxLoop, keep int64, min, max, stopFitness, r1 float64, r2 float64)
	//设置自定义适应度函数
	SetFuncHandler(f GetFitnessHandler)
}

type MainProcess struct {
	NumberOfChromosome int64
	LengthOfChromosome int64
	Min                float64
	Max                float64
	Keep               int64
	Kill               int64
	LoopNow            int64
	MaxFitness         float64
	SelectChromosome   []ChromosomeInterface
	NewChromosome      []ChromosomeInterface
	R                  float64
	PopulationObj      PopulationInterface
	Debug              bool
	F                  GetFitnessHandler
}

func NewMainProcess() MainProcessInterface {
	return &MainProcess{}
}

func (m *MainProcess) init() {
	m.PopulationObj = BuildRandPopulation(m.NumberOfChromosome, m.LengthOfChromosome, m.Min, m.Max, m.F)
	m.LoopNow = 0
	m.MaxFitness = 0.0
	m.SelectChromosome = make([]ChromosomeInterface, m.Kill*2)
	m.NewChromosome = make([]ChromosomeInterface, m.Kill)
}

func (m *MainProcess) sort() {
	if m.Keep != 1 {
		m.PopulationObj.Sort()
	}
}

func (m *MainProcess) selectChromosome() {
	var select1, select2 ChromosomeInterface
	generate := 2 * m.Kill
	//rand.Seed(time.Now().Unix())
	for i := int64(0); i < generate; i++ {
		select1 = m.PopulationObj.GetChromosom(rand.Int63n(m.NumberOfChromosome))
		select2 = m.PopulationObj.GetChromosom(rand.Int63n(m.NumberOfChromosome))
		if select1.GetFitness() > select2.GetFitness() {
			m.SelectChromosome[i] = select1
		} else {
			m.SelectChromosome[i] = select2
		}
	}
}

func (m *MainProcess) crossover() {
	for i := int64(0); i < m.Kill; i++ {
		m.NewChromosome[i] = m.SelectChromosome[2*i].Crossover(m.SelectChromosome[1+2*i])
	}
}

func (m *MainProcess) mutation() {
	if m.R < 0 {
		return
	}
	for i := int64(0); i < m.Kill; i++ {
		m.NewChromosome[i].Mutation(m.R)
	}
}

func (m *MainProcess) generated() {
	if m.Keep != 1 {
		for i := m.Keep; i < m.NumberOfChromosome; i++ {
			m.PopulationObj.ReplaceChromosom(i, m.NewChromosome[i-m.Keep])
		}
	} else {
		var replaceOffset int64 = 0
		var NewOffset int64 = 0
		maxFitnessChromosome := m.PopulationObj.GetMaxFitnessChromosome()
		for i := int64(0); i < m.NumberOfChromosome; i++ {
			if m.PopulationObj.GetChromosom(replaceOffset) != maxFitnessChromosome {
				m.PopulationObj.ReplaceChromosom(replaceOffset, m.NewChromosome[NewOffset])
				NewOffset++
			}
			replaceOffset++
		}
	}
}

func (m *MainProcess) Run(num, length, maxLoop, keep int64, min, max, stopFitness, r float64, r2 float64) {
	m.NumberOfChromosome = num
	m.LengthOfChromosome = length
	m.Min = min
	m.Max = max
	m.Keep = keep
	m.Kill = num - keep
	m.R = r

	m.init()
	m.sort()
	m.MaxFitness = m.PopulationObj.GetMaxFitnessChromosome().GetFitness()

	for m.LoopNow < maxLoop && m.MaxFitness < stopFitness {
		m.selectChromosome()
		m.crossover()
		m.mutation()
		m.generated()
		m.sort()
		m.MaxFitness = m.PopulationObj.GetMaxFitnessChromosome().GetFitness()
		if m.MaxFitness > stopFitness/2 {
			m.R = r2
		}
		m.LoopNow++
	}
	fmt.Print("loop=", m.LoopNow, ", maxfitness=", m.MaxFitness,
		", chromosome dump=   ",
	)
	m.PopulationObj.GetMaxFitnessChromosome().Dump()
	fmt.Println("end!!!!!!")

}

func (m *MainProcess) SetFuncHandler(f GetFitnessHandler) {
	m.F = f
}
